(in-package #:experiment)

(gwee:define-component stimulus-response (state)
    ((stimulus :initarg :stimulus)
     (response :initarg :response)
     (feedback :initarg :feedback)
     (presentation :initform nil :initarg :presentation)
     (presentation-duration :initform nil :initarg :presentation-duration)
     (stimulus-duration :initform nil :initarg :stimulus-duration)
     (feedback-duration :initform nil :initarg :feedback-duration))
    (:views
     (presentation-text gwee:text-area :data stimulus
                                       :font (gwee:open-font "serif.ttf" 50))
     (presentation-help gwee:text-area :data "Press ENTER when you know the answer"
                                       :foreground (gwee:gray 100)
                                       :vertical-justification :center
                                       :x :center)
     (presentation-space gwee:text-area
                         :font (gwee:open-font "serif.ttf" 48)
                         :data "")
     (stimulus-text gwee:text-area :data stimulus
                                   :font (gwee:open-font "serif.ttf" 50))
     (stimulus-help gwee:text-area :data "Press ENTER to submit"
                                   :foreground (gwee:gray 100)
                                   :vertical-justification :center
                                   :x :center)
     (feedback-text gwee:text-area :data feedback
                                   :font (gwee:open-font "serif.ttf" 50))
     (feedback-help gwee:text-area :data " "
                                   :foreground (gwee:gray 100)
                                   :vertical-justification :center
                                   :x :center)
     (feedback-input input-widget:input
                     :border-color nil)
     (input input-widget:input))
    (:layouts
     (default gwee:switchable-layout
              (presentation-layout stimulus-layout feedback-layout))
     (presentation-layout gwee:column-layout
                          (presentation-text
                           presentation-space
                           presentation-help)
                          :padding 10)
     (stimulus-layout gwee:column-layout
                      (stimulus-text
                       input
                       stimulus-help)
                      :padding 10)
     (feedback-layout gwee:column-layout
                      (feedback-text feedback-input feedback-help)
                      :padding 10)))

(defmethod exp:state-dump append (exp (state stimulus-response))
  (declare (ignore exp))
  (with-slots (stimulus response feedback presentation presentation-duration
               stimulus-duration feedback-duration) state
    `(:stimulus ,stimulus
      :response ,response
      :feedback ,feedback
      :presentation ,presentation
      :presentation-duration ,presentation-duration
      :stimulus-duration ,stimulus-duration
      :feedback-duration ,feedback-duration)))

(defmethod state-init (exp (state stimulus-response))
  (declare (ignore exp))
  (with-slots (presentation stimulus response feedback
               presentation-duration default
               stimulus-layout
               stimulus-duration feedback-duration feedback-help) state
    (lg 'start :stimulus stimulus :response response :feedback feedback :presentation presentation)
    (unless feedback-duration
      (setf (gwee:view-data feedback-help) "Press ENTER to continue"))
    (if presentation
        (when presentation-duration
          (start-timer presentation-duration :tag :presentation-timer))
        (progn
          (setf (gwee:switchable-layout-current-view default) stimulus-layout)
          (when stimulus-duration
            (start-timer stimulus-duration :tag :stimulus-timer))))))

(define-state-match presentation ((state stimulus-response))
  (with-slots (default presentation-layout) state
    (eq (gwee:switchable-layout-current-view default) presentation-layout)))

(define-state-match stimulus ((state stimulus-response))
  (with-slots (default stimulus-layout) state
    (eq (gwee:switchable-layout-current-view default) stimulus-layout)))

(define-state-match feedback ((state stimulus-response))
  (with-slots (default feedback-layout) state
    (eq (gwee:switchable-layout-current-view default) feedback-layout)))

(define-action presentation-submit ((state stimulus-response)
                                (raw-keypress :var key)
                                (presentation))
  (with-slots (default stimulus-layout stimulus-duration) state
    (let ((ch (gwee:key-to-character key)))
      (when (and (characterp ch)
                 (char= ch #\Return))
        (lg 'start-input)
        (setf (gwee:switchable-layout-current-view default) stimulus-layout)
        (when stimulus-duration
          (start-timer stimulus-duration :tag :stimulus-timer))))))

(define-action presentation-timeout ((state stimulus-response)
                                     (timeout :tag :presentation-timer)
                                     (presentation))
  (advance-to-feedback state t))

(define-action input ((state stimulus-response)
                      (raw-keypress :var key)
                      (stimulus))
  (with-slots (input) state
    (let ((ch (gwee:key-to-character key)))
      (when (characterp ch)
        (cond
          ((char= ch #\Return)
           (advance-to-feedback state nil))
          ((or (alphanumericp ch)
               (find ch '(#\. #\* #\/ #\+ #\- #\=) :test 'char=))
           (lg 'keypress :key ch)
           (input-widget:input-add input ch)))))))

(define-action stimulus-timeout ((state stimulus-response)
                                 (timeout :tag :stimulus-timer)
                                 (stimulus))
  (advance-to-feedback state t))

(define-action feedback-timeout ((state stimulus-response)
                                 (timeout :tag :feedback-timer)
                                 (feedback))
  (state-onward *experiment* state))

(define-action feedback-submit ((state stimulus-response)
                                (raw-keypress :var key)
                                (feedback))
  (with-slots (feedback-duration) state
    (let ((ch (gwee:key-to-character key)))
      (when (and (not feedback-duration)
                 (characterp ch)
                 (char= ch #\Return))
        (state-onward *experiment* state)))))

(defun advance-to-feedback (state timeout)
  (with-slots (default feedback-layout feedback-input input feedback response feedback-text
               feedback-duration) state
    (let ((correctp (and (not timeout)
                         (string= response (gwee:view-data input)))))
      (exp:lg 'feedback :how (if timeout :timeout :submit) :answer (gwee:view-data input) :correctp correctp)
      (setf (gwee:switchable-layout-current-view default) feedback-layout
            (gwee:view-data feedback-input) (if timeout "Time's up!" (gwee:view-data input))
            (gwee:foreground-color feedback-text) (if correctp
                                                      (gwee:color 0 255 0)
                                                      (gwee:color 255 0 0)))
      (input-widget::recenter feedback-input)
      (when feedback-duration
        (start-timer feedback-duration :tag :feedback-timer)))))
